import datetime
import json
import logging
from enum import Enum, unique
from itertools import chain
from typing import Any

import requests
from django.core.handlers.wsgi import WSGIRequest
from django.core.paginator import Paginator
from django.db import transaction
from django.db.models import Value, F, Sum
from django.http import JsonResponse
from django.views import View
from rest_framework import status
from rest_framework.parsers import JSONParser

from jqexception.jqexecption import (
    NicknameIsTooLongErr,
    jq_try,
    UnknowErr,
    NullErr,
    ReqMethodErr,
    ValueErr,
)
from jqgame.basegame import GameType
from jqpackage.error import ParameterNullErr
from jqpage.pagination import JQPagination
from juqiadmin.auth import login_auth
from juqiadmin.common.filters import (
    StringFilterResolver,
    EnumFilterResolver,
    join_filters,
)
from juqiadmin.errer import GameRuleNullErr
from juqiadmin.models import (
    get_wxuser_recharge_withdraw_finance,
    FinanceType,
    Finance,
    GameRule,
)
from juqiadmin.serializers import GameRuleSerializer
from quanjushang.models import QuanjushangGame
from warehouse.models import JuqiGood
from wuxianshang.models import WuxianshangGame
from wxcloudrun.models import get_web_settings
from wxuser.wxpay import unifiedorder
from yifanshang.models import YifanshangGame
from . import wxapi
from .error import UserNotExistentErr
from .models import (
    OrderState,
    UserHunJingRecord,
    UserScoreRecord,
    Users,
    UserStoredMoneyRecord,
    UserPackage,
    UserRechargeOrder,
    refined_user_order_goods,
    UserOrder,
    UserRefinedOrder,
)
from .serializers import (
    UserHunJingRecordSerializer,
    UserScoreRecordSerializer,
    UserSerializer,
    get_detail_UserPackage,
    UserRechargeOrderSerializer,
    UserStoredMoneyRecordSerializer,
    GameListSerializer,
    UserOrderSerializer,
    UserHistoryRefiningSerializer,
)
from .utli import get_current_user

logger = logging.getLogger("log")


# Create your views here.


class WxUserView(View):
    def setup(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> None:
        super().setup(request, *args, **kwargs)

    @jq_try
    def get(self, request, *args, **kwargs):
        openid = request.META.get("HTTP_X_WX_OPENID", None)

        u = Users.objects.filter(openid=openid).first()
        u_s = UserSerializer(u)
        return JsonResponse(u_s.data, status=status.HTTP_200_OK)

    @jq_try
    @transaction.atomic
    def post(self, request, *args, **kwargs):
        openid = request.META.get("HTTP_X_WX_OPENID", None)

        u = JSONParser().parse(request)
        if len(u["nickname"]) > 20:
            raise NicknameIsTooLongErr

        u["openid"] = openid
        u_s = UserSerializer(data=u)
        u_s.is_valid(raise_exception=True)
        u_s.save()
        return JsonResponse(u_s.data, status=status.HTTP_201_CREATED)

    @jq_try
    @transaction.atomic
    def put(self, request, *args, **kwargs):
        openid = request.META.get("HTTP_X_WX_OPENID", None)

        u_data: dict = JSONParser().parse(request)

        for field in ["stored_money", "hunjing", "score"]:
            try:
                del u_data[field]
            except:
                logger.error(f"can not change user {field}. user: {openid}")

        u = (
            Users.objects.filter(openid=openid)
            .filter(deleted=False)
            .select_for_update()
            .first()
        )

        if not u:
            raise NullErr

        for k, v in u_data.items():
            setattr(u, k, v)
        u.save()

        u_s = UserSerializer(u)
        return JsonResponse(u_s.data, status=status.HTTP_200_OK)

    @jq_try
    def delete(self, request, *args, **kwargs):
        raise UnknowErr


@jq_try
@transaction.atomic
def telephone(request: WSGIRequest, _):
    if request.method != "POST":
        raise ReqMethodErr

    openid = request.META.get("HTTP_X_WX_OPENID", None)
    if not openid:
        raise NullErr

    getopendata_url = f"{wxapi.getopendata}?openid={openid}"
    o: dict = json.loads(request.body)
    cloudID = o.get("cloudID", "")

    if not cloudID:
        raise UnknowErr

    res = requests.post(
        getopendata_url,
        json={"cloudid_list": [cloudID]},
        headers={
            "Content-Type": "application/json",
        },
    )
    logger.debug(res.content)

    #    const data = JSON.parse(body).data_list[0]; // 从回包中获取手机号信息
    #    const phone = JSON.parse(data.json).data.phoneNumber;
    d = json.loads(res.content)

    phone = (
        json.loads(d.get("data_list", [{}])[0].get("json", "{}"))
        .get("data", {})
        .get("phoneNumber", None)
    )
    if not phone:
        raise NullErr

    u = (
        Users.objects.filter(openid=openid)
        .filter(deleted=False)
        .select_for_update()
        .first()
    )
    if not u:
        raise NullErr

    u.telephone = phone
    u.save()

    logger.debug(d)

    u_s = UserSerializer(u)
    return JsonResponse(u_s.data, status=status.HTTP_200_OK)


# http://127.0.0.1:27082/api/wxuser/userpackage
class UserPackageView(View):
    def setup(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> None:
        super().setup(request, *args, **kwargs)

    #
    @login_auth
    def get(self, request, *args, **kwargs):
        # Perform io-blocking view logic using await, sleep for example.
        openid = request.META.get("HTTP_X_WX_OPENID", None)
        user = Users.objects.get(openid=openid)

        res = {}
        res["goods"] = get_detail_UserPackage(user)
        return JsonResponse(res)

    @jq_try
    @transaction.atomic
    def post(self, request, *args, **kwargs):
        # Perform io-blocking view logic using await, sleep for example.
        pass

    @login_auth
    @transaction.atomic
    def delete(self, request, *args, **kwargs):
        # 用户提炼
        openid = request.META.get("HTTP_X_WX_OPENID", None)
        user = Users.objects.filter(deleted=False).get(openid=openid)

        o: dict = json.loads(request.body)
        good_uuid = o["good_uuid"]
        count = int(o["count"])
        if count < 1:
            raise ValueErr

        good = JuqiGood.objects.filter(uuid=good_uuid).filter(deleted=False).first()
        if not good:
            raise ValueErr

        userPackage = (
            UserPackage.objects.filter(good_uuid=good_uuid)
            .filter(user_uuid=user.uuid)
            .filter(deleted=False)
            .select_for_update()
            .first()
        )
        if not userPackage:
            raise ValueErr

        if count > userPackage.count:
            raise ValueErr
        userPackage.count -= count
        if userPackage.count == 0:
            userPackage.deleted = True
        userPackage.save()

        openid = request.META.get("HTTP_X_WX_OPENID", None)
        user = (
            Users.objects.filter(openid=openid)
            .filter(deleted=False)
            .select_for_update()
            .first()
        )
        if not user:
            raise ValueErr

        sum_good_value = good.price * count
        _old = user.hunjing
        _new = _old + sum_good_value
        user.hunjing = _new
        _record = {
            "user_uuid": user.uuid,
            "hunjing": sum_good_value,
            "old_hunjing": _old,
            "new_hunjing": _new,
            "detail": f"获得魂晶 {sum_good_value}: {good.name} {good.price} {count}个",
        }
        UserHunJingRecord(**_record).save()

        user.hunjing = _new
        user.save()
        refined_user_order_goods(user.uuid, good_uuid, count)  # 更新用户订单关联物品状态为提炼
        res = {"user": UserSerializer(user).data, "goods": get_detail_UserPackage(user)}
        return JsonResponse(res, status=status.HTTP_200_OK)


class UserPayView(View):
    def setup(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> None:
        openid = request.META.get("HTTP_X_WX_OPENID", None)
        self.user = Users.objects.get(openid=openid)

        super().setup(request, *args, **kwargs)

    #
    @jq_try
    def get(self, request, *args, **kwargs):
        # Perform io-blocking view logic using await, sleep for example.
        #   req query params：
        #       query many
        #           - `many`  查询所有商品
        #           - `page` 分页
        #           - `num`  查询个数
        #           - `openid` 按照 个人openid 查询
        #       query single
        #           - `out_trade_no` 单笔交易单号
        _list = request.GET.get("list", None)
        if not _list:
            # query single good
            out_trade_no = request.GET.get("out_trade_no", None)
            if not out_trade_no:
                raise ValueErr

            uro = UserRechargeOrder.objects.get(out_trade_no=out_trade_no)
            uro_s = UserRechargeOrderSerializer(uro)

            return JsonResponse(uro_s.data, status=status.HTTP_200_OK)
        else:
            page = int(request.GET.get("page", None))
            num = min(50, int(request.GET.get("num", None)))
            openid = request.META.get("HTTP_X_WX_OPENID", None)

            condition = {"deleted": False}
            if openid:
                condition["openid"] = openid

            _all = UserRechargeOrder.objects.filter(**condition)
            p = Paginator(_all, per_page=num)
            objs = p.get_page(page).object_list
            objs_s = UserRechargeOrderSerializer(objs, many=True)
            return JsonResponse(objs_s.data, status=status.HTTP_200_OK, safe=False)

    @jq_try
    @transaction.atomic
    def put(self, request, *args, **kwargs):
        # Perform io-blocking view logic using await, sleep for example.
        o: dict = json.loads(request.body)
        total_fee = int(o.get("total_fee", 0))

        data, out_trade_no = unifiedorder(
            request,
            total_fee=total_fee,
            call_back_url="/api/wxuser/pay_callback",
            order_title="用户充值",
        )

        payment = data.get("respdata").get("payment")

        return JsonResponse(
            {"code": 0, "payment": payment, "out_trade_no": out_trade_no},
            status=status.HTTP_200_OK,
        )


@jq_try
@transaction.atomic
def pay_callback(request, *args, **kwargs):
    # https://developers.weixin.qq.com/miniprogram/dev/wxcloudrun/src/development/pay/callback/

    if request.method != "POST":
        raise ReqMethodErr

    o: dict = json.loads(request.body)
    logger.debug(f"pay_callback request.body: {o}")

    resultCode = o.get("resultCode", "").strip()
    if resultCode != "SUCCESS":
        raise ValueErr

    openid = o.get("subOpenid", None)
    if not openid:
        raise NullErr

    total_fee = int(o.get("totalFee", None))
    if total_fee < 1:
        raise ValueErr

    u = (
        Users.objects.filter(openid=openid)
        .filter(deleted=False)
        .select_for_update()
        .first()
    )
    if not u:
        raise NullErr

    old_stored_money = u.stored_money
    new_stored_money = old_stored_money + total_fee
    u.stored_money = new_stored_money
    u.save()

    # 记录微信充值
    finance = get_wxuser_recharge_withdraw_finance(u.uuid)
    finance.all_recharge += total_fee
    finance.save()

    params = {
        "user_uuid": u.uuid,
        "stored_money": total_fee,
        "old_stored_money": old_stored_money,
        "new_stored_money": new_stored_money,
        "detail": "小程序充值",
    }
    UserStoredMoneyRecord(**params).save()

    out_trade_no = o.get("outTradeNo", None)
    if not out_trade_no:
        raise ValueErr
    uro = UserRechargeOrder.objects.get(out_trade_no=out_trade_no)
    uro.state = OrderState.success.value
    uro.save()

    data = {"errcode": 0, "errmsg": ""}
    return JsonResponse(data, status=status.HTTP_200_OK)


@unique
class RecordType(Enum):
    all = 0
    income = 1
    expend = 2


class UserWealthRecordView(View):
    def setup(self, request: WSGIRequest, *args: Any, **kwargs: Any) -> None:
        super().setup(request, *args, **kwargs)

    #
    @login_auth
    def get(self, request, *args, **kwargs):
        # Perform io-blocking view logic using await, sleep for example.
        _list = request.GET.get("list", None)
        if _list:
            query_type = request.GET.get("query_type", None)
            record_type_value = EnumFilterResolver(RecordType).resolve_argument(
                "record_type", request
            )
            if record_type_value is None:
                record_type_value = 0
            record_type = RecordType(record_type_value)
            openid = request.META.get("HTTP_X_WX_OPENID", None)
            user = Users.objects.get(openid=openid)

            _m = {
                "stored_money": {
                    "model": UserStoredMoneyRecord,
                    "ser": UserStoredMoneyRecordSerializer,
                },
                "hunjing": {
                    "model": UserHunJingRecord,
                    "ser": UserHunJingRecordSerializer,
                },
                "score": {
                    "model": UserScoreRecord,
                    "ser": UserScoreRecordSerializer,
                },
            }
            _model = _m[query_type]["model"]
            _ser = _m[query_type]["ser"]

            _l = _model.objects.filter(user_uuid=user.uuid).filter(deleted=False)

            if record_type != RecordType.all:
                _query = {}
                if record_type == RecordType.income:
                    _query = {f"{query_type}__gt": 0}
                elif record_type == RecordType.expend:
                    _query = {f"{query_type}__lt": 0}
                _l = _l.filter(**_query)
            p = JQPagination(_ser)
            data = p.paginate(_l, request)

            return JsonResponse(data, safe=False, status=status.HTTP_200_OK)

        raise UnknowErr


@unique
class WanFaMode(Enum):
    ordinary = 0
    score = 1
    baodi = 2
    tianjiangsb = 3
    feiqiu = 4


class GameListView(View):
    @jq_try
    def get(self, request, *args, **kwargs):
        # 1 score、2 baodi、3 tianjiangsb、4 feiqiu
        wanfa = WanFaMode(int(request.GET.get("wanfa", 0)))
        openid = request.META.get("HTTP_X_WX_OPENID", None)
        is_login = True
        if openid is None or openid == "":
            is_login = False

        game_type = EnumFilterResolver(GameType).resolve_argument("game_type", request)
        is_meet = True
        if is_login:
            web_settings = get_web_settings()
            if web_settings.recharge_page_switch:
                openid = request.META.get("HTTP_X_WX_OPENID", None)
                _query_set = Users.objects.filter(openid=openid)
                user = _query_set.first()
                if user is not None:
                    finance = (
                        Finance.objects.filter(deleted=False)
                        .filter(finance_type=FinanceType.wxuser.value)
                        .filter(user_uuid=user.uuid)
                        .values("user_uuid")
                        .annotate(
                            sum_price=Sum(F("all_recharge") + F("all_admin_recharge"))
                        )
                        .values("sum_price")
                    )
                    if finance.count() > 0:
                        finance = finance[0]
                        sum_price = finance.get("sum_price")
                        if sum_price is not None and sum_price != 0:
                            if sum_price >= web_settings.hidden_box_consume:
                                is_meet = True
                            else:
                                is_meet = False
                        else:
                            is_meet = False
                    else:
                        is_meet = False
                else:
                    is_meet = False
        else:
            is_meet = False

        _qj_query_set = []
        _wx_query_set = []
        _yf_query_set = []

        if game_type is None or game_type == GameType.quanjushang.value:
            if wanfa == WanFaMode.score:
                _qj_query_set = (
                    QuanjushangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(score_pay=1)
                )
                if not is_meet:
                    _qj_query_set = _qj_query_set.filter(hide_stats=0)

                _qj_query_set = _qj_query_set.annotate(
                    game_type=Value(GameType.quanjushang.value)
                ).values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "is_care",
                    "is_ccs",
                    "is_fjf",
                    "score_pay",
                )
            elif wanfa == WanFaMode.ordinary:
                _qj_query_set = (
                    QuanjushangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(score_pay=0)
                    .annotate(game_type=Value(GameType.quanjushang.value))
                )
                if not is_meet:
                    _qj_query_set = _qj_query_set.filter(hide_stats=0)

                _qj_query_set = _qj_query_set.values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "is_care",
                    "is_ccs",
                    "is_fjf",
                    "score_pay",
                )

        if game_type is None or game_type == GameType.wuxianshang.value:
            if wanfa == WanFaMode.score:
                _wx_query_set = (
                    WuxianshangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(score_pay=1)
                )
                if not is_meet:
                    _wx_query_set = _wx_query_set.filter(is_hide=0)
                _wx_query_set = _wx_query_set.annotate(
                    game_type=Value(GameType.wuxianshang.value),
                    cover_url=F("display_url"),
                ).values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "score_pay",
                    "baodi",
                    "tianjiangsb",
                    "feiqiu",
                    "feiqiu_num",
                )
            elif wanfa == WanFaMode.baodi:
                _wx_query_set = (
                    WuxianshangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(baodi=1)
                )
                if not is_meet:
                    _wx_query_set = _wx_query_set.filter(is_hide=0)
                _wx_query_set = _wx_query_set.annotate(
                    game_type=Value(GameType.wuxianshang.value),
                    cover_url=F("display_url"),
                ).values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "score_pay",
                    "baodi",
                    "tianjiangsb",
                    "feiqiu",
                    "feiqiu_num",
                )
            elif wanfa == WanFaMode.tianjiangsb:
                _wx_query_set = (
                    WuxianshangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(tianjiangsb=1)
                )
                if not is_meet:
                    _wx_query_set = _wx_query_set.filter(is_hide=0)
                _wx_query_set = _wx_query_set.annotate(
                    game_type=Value(GameType.wuxianshang.value),
                    cover_url=F("display_url"),
                ).values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "score_pay",
                    "baodi",
                    "tianjiangsb",
                    "feiqiu",
                    "feiqiu_num",
                )
            elif wanfa == WanFaMode.ordinary:
                _wx_query_set = (
                    WuxianshangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(score_pay=0)
                )
                if not is_meet:
                    _wx_query_set = _wx_query_set.filter(is_hide=0)

                _wx_query_set = _wx_query_set.annotate(
                    game_type=Value(GameType.wuxianshang.value),
                    cover_url=F("display_url"),
                ).values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "score_pay",
                    "baodi",
                    "tianjiangsb",
                    "feiqiu",
                    "feiqiu_num",
                )
            elif wanfa == WanFaMode.feiqiu:
                _wx_query_set = (
                    WuxianshangGame.objects.filter(deleted=False)
                    .filter(state=1)
                    .filter(feiqiu=1)
                )
                if not is_meet:
                    _wx_query_set = _wx_query_set.filter(is_hide=0)

                _wx_query_set = _wx_query_set.annotate(
                    game_type=Value(GameType.wuxianshang.value),
                    cover_url=F("display_url"),
                ).values(
                    "uuid",
                    "name",
                    "cover_url",
                    "game_type",
                    "price",
                    "createdAt",
                    "score_pay",
                    "baodi",
                    "tianjiangsb",
                    "feiqiu",
                    "feiqiu_num",
                )

        if game_type is None or game_type == GameType.yifanshang.value:
            if wanfa == WanFaMode.ordinary:
                _yf_query_set = YifanshangGame.objects.filter(deleted=False).filter(
                    state=1
                )
                if not is_meet:
                    _yf_query_set = _yf_query_set.filter(is_hide=0)

                _yf_query_set = _yf_query_set.annotate(
                    game_type=Value(GameType.yifanshang.value),
                ).values("uuid", "name", "cover_url", "game_type", "price", "createdAt")

        _list = []
        if game_type is None:
            _list = chain(_wx_query_set, _qj_query_set, _yf_query_set)
        elif game_type == GameType.wuxianshang.value:
            _list = _wx_query_set
        elif game_type == GameType.quanjushang.value:
            _list = _qj_query_set
        elif game_type == GameType.yifanshang.value:
            _list = _yf_query_set
        # if len(_list) == 0:
        #     raise NullErr
        _data_list = []
        for item in _list:
            _data_list.append(GameListSerializer(item).data)

        return JsonResponse(
            {"code": "0", "msg": "ok", "data": _data_list}, status=status.HTTP_200_OK
        )


class UserHistoryRechargeView(View):
    # 用户端消费记录
    @login_auth
    def get(self, request, *args, **kwargs):
        openid = request.META.get("HTTP_X_WX_OPENID", None)
        if openid is None:
            raise ParameterNullErr
        user = Users.objects.filter(openid=openid).first()
        if user is None:
            raise UserNotExistentErr

        _query_set = UserOrder.objects.filter(user_uuid=user.uuid).all()
        pagination = JQPagination(UserOrderSerializer)
        _data = pagination.paginate(_query_set, request)

        _date = datetime.datetime.now()
        today_price = (
            Finance.objects.filter(deleted=False)
            .filter(createdAt__year=_date.year)
            .filter(createdAt__month=_date.month)
            .filter(createdAt__day=_date.day)
            .filter(finance_type=FinanceType.wxuser.value)
            .filter(user_uuid=user.uuid)
            .values("user_uuid")
            .annotate(today_price=Sum("all_raffle_income"))
            .values_list("today_price", flat=True)
        )

        if today_price is None or len(today_price) <= 0:
            today_price = [0]
        _data["today_price"] = today_price[0]
        return JsonResponse(
            {
                "code": 0,
                "msg": "ok",
                "data": _data,
            },
            status=status.HTTP_200_OK,
        )


class UserHistoryRefiningView(View):
    # 用户端提炼记录
    @login_auth
    def get(self, request, *args, **kwargs):
        openid = request.META.get("HTTP_X_WX_OPENID", None)
        if openid is None:
            raise ParameterNullErr
        user = Users.objects.filter(openid=openid).first()
        if user is None:
            raise UserNotExistentErr
        _all = (
            UserRefinedOrder.objects.filter(deleted=False)
            .filter(user_uuid=user.uuid)
            .all()
        )

        pagination = JQPagination(UserHistoryRefiningSerializer)
        _data = pagination.paginate(_all, request)

        return JsonResponse(
            {"code": 0, "msg": "ok", "data": _data},
            status=status.HTTP_200_OK,
        )


class WxGameRuleView(View):
    _filters = {
        "uuid": StringFilterResolver(required=True),
    }

    @jq_try
    def get(self, request: WSGIRequest, *args, **kwargs):
        _query_set = GameRule.objects.filter(deleted=False)
        _query_set = join_filters(_query_set, request, self._filters)
        game_rule = _query_set.first()

        if game_rule is None:
            raise GameRuleNullErr

        return JsonResponse(
            {"code": 0, "msg": "ok", "data": GameRuleSerializer(game_rule).data},
            status=status.HTTP_200_OK,
        )
